package com.xzzz.irda.tracker.server;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import com.xzzz.common.base.pojo.PageRes;
import com.xzzz.common.base.util.BeanUtil;
import com.xzzz.irda.tracker.core.SpanNode;
import com.xzzz.irda.tracker.core.common.TrackerConstants;
import com.xzzz.irda.tracker.core.repository.TrackerRepository;
import com.xzzz.irda.tracker.server.pojo.SpanNodeTermsRes;
import com.xzzz.irda.tracker.server.pojo.SpanNodeTreeRes;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * Tracker service 服务器
 * <p> Tracker 通常作为远程服务器接收客户端发来的请求, 例如 {@link TrackerServerConfiguration} 就是通过
 * Http 接口的方式接收客户端发来的请求, 当然也可以实现该接口来拓展服务方接收信息的方式.
 */
public interface TrackerMetricService {

    /**
     * 批量保存 span 集合, 该接口用来接收客户端发来的 span 批量信息, 然后保存.
     * <p>具体保存方式由接口实现, 例如将 span 保存在数据库中. 这里推荐具体的保存
     * 逻辑通过实现 {@link TrackerRepository} 接口来实现, 并在该接收的实现类
     * 中注入 {@link TrackerRepository} 的实现类, 并调用 {@link TrackerRepository#save(List)}
     * 来保存, 这样保存 span 信息的逻辑实现是唯一的。
     *
     *
     * @param spanNodes span 集合
     */
    void saveBatch(List<SpanNode> spanNodes);

    /**
     * 查询 span 集合
     *
     * @param traceId traceId
     * @return span 集合
     */
    List<SpanNode> list(String traceId);

    /**
     * 分页
     * @param pageNum
     * @param pageSize
     * @param appName
     * @param traceId
     * @return
     */
    PageRes<SpanNode> pages(Integer pageNum, Integer pageSize, String appName, String traceId);

    /**
     * 根据 traceId 分组聚合查询
     *
     * @param pageSize 页数
     * @param appName 应用名称
     * @param traceId 追踪ID
     */
    List<SpanNodeTermsRes> listGroupByTraceId(Integer pageSize, String appName, String traceId);


    /**
     * 为孤儿节点填充父节点
     *
     * @param spans
     * @return
     */
    default List<SpanNode> fillParentWithOrphan(List<SpanNode> spans) {
        // 将所有节点根据 spanId 保存至 map, 方便读取
        Map<String, SpanNode> spanMap = spans.stream().collect(Collectors.toMap(SpanNode::getSpanId, span -> span));

        // 孤儿节点
        List<SpanNode> orphans = new ArrayList<>();
        for (SpanNode span : spans) {
            // 如果 span 没有找到 parent, 则认为是孤儿 span
            if (!span.getSpanParentId().equals(TrackerConstants.ROOT_SPAN) && spanMap.get(span.getSpanParentId()) == null) {
                orphans.add(span);
            }
        }

        // 没有孤儿节点, 则直接返回
        if (CollUtil.isEmpty(orphans)) {
            return spans;
        }

        // 保存所有虚拟父节点
        List<SpanNode> virtualParents = new ArrayList<>();

        // 将孤儿节点的父节点分组, 得出需要生成的虚拟父节点个数, 及子节点集合
        Map<String, List<SpanNode>> orphansGroupByParentId = orphans.stream().collect(Collectors.groupingBy(SpanNode::getSpanParentId));

        final SpanNode ROOT;
        if (spans.stream().anyMatch(spanNode -> spanNode.getSpanParentId().equals(TrackerConstants.ROOT_SPAN))) {
            ROOT = spans.stream().filter(spanNode -> spanNode.getSpanParentId().equals(TrackerConstants.ROOT_SPAN)).collect(Collectors.toList()).get(0);
        } else {
            SpanNode minStart = spans.stream().min(Comparator.comparingLong(SpanNode::getSpanStart)).get();
            SpanNode maxEnd = spans.stream().max(Comparator.comparingLong(SpanNode::getSpanEnd)).get();
            ROOT = new SpanNode();
            ROOT.setAppName("虚拟根节点");
            ROOT.setSpanName("根节点尚未持久化成功,请稍后再试");
            ROOT.setSpanType("VIRTUAL_ROOT_SPAN");
            ROOT.setSpanParentId(TrackerConstants.ROOT_SPAN);
            ROOT.setSpanId(UUID.fastUUID().toString(true));
            ROOT.setTraceId(spans.get(0).getTraceId());
            ROOT.setSpanStart(minStart.getSpanStart());
            ROOT.setSpanEnd(maxEnd.getSpanEnd());
            virtualParents.add(ROOT);
        }

        // 生成虚拟父节点
        orphansGroupByParentId.forEach((parentId, orphanList) -> {
            SpanNode minStart = orphanList.stream().min(Comparator.comparingLong(SpanNode::getSpanStart)).get();
            SpanNode maxEnd = orphanList.stream().max(Comparator.comparingLong(SpanNode::getSpanEnd)).get();
            SpanNode virtualParent = new SpanNode();
            virtualParent.setAppName("虚拟节点");
            virtualParent.setSpanName("该节点尚未持久化成功,请稍后再试");
            virtualParent.setSpanType("VIRTUAL_SPAN");
            virtualParent.setSpanParentId(ROOT.getSpanId());
            virtualParent.setSpanId(parentId);
            virtualParent.setTraceId(spans.get(0).getTraceId());
            virtualParent.setSpanStart(minStart.getSpanStart());
            virtualParent.setSpanEnd(maxEnd.getSpanEnd());
            virtualParents.add(virtualParent);
        });

//        if (virtualParents.size() > 1) {
//            SpanNode minStart = spans.stream().min(Comparator.comparingLong(SpanNode::getSpanStart)).get();
//            SpanNode maxEnd = spans.stream().max(Comparator.comparingLong(SpanNode::getSpanEnd)).get();
//
//            SpanNode virtualRoot = new SpanNode();
//            virtualRoot.setAppName("NONE");
//            virtualRoot.setSpanName("该节点尚未持久化成功,请稍后再试");
//            virtualRoot.setSpanType("VIRTUAL_SPAN");
//            virtualRoot.setSpanParentId(null);
//            virtualRoot.setSpanId("NONE");
//            virtualRoot.setTraceId(spans.get(0).getTraceId());
//            virtualRoot.setSpanStart(minStart.getSpanStart());
//            virtualRoot.setSpanEnd(maxEnd.getSpanEnd());
//            for (SpanNode virtualParent : virtualParents) {
//                virtualParent.setSpanParentId("NONE");
//            }
//            virtualParents.add(virtualRoot);
//        }

        spans.addAll(virtualParents);
        return spans;
    }

    /**
     * 递归排序
     *
     * @param spans
     * @param result
     * @return
     */
    default List<SpanNode> sortSpans(List<SpanNode> spans, List<SpanNode> result) {
        // 如果没有结果集，则创建结果集并取出根节点
        if (result == null) {
            result = new CopyOnWriteArrayList<>();
            Iterator<SpanNode> iterator = spans.iterator();
            while (iterator.hasNext()) {
                SpanNode span = iterator.next();
                if (span.getSpanParentId().equals(TrackerConstants.ROOT_SPAN)) {
                    result.add(span);
                    iterator.remove();
                }
            }
            sortSpans(spans, result);
        }
        // 有结果集
        else if (CollUtil.isNotEmpty(spans)) {
            for (SpanNode parent : result) {
                Iterator<SpanNode> iterator = spans.iterator();
                while (iterator.hasNext()) {
                    SpanNode span = iterator.next();
                    if (span.getSpanParentId().equals(parent.getSpanId())) {
                        result.add(span);
                        iterator.remove();
                    }
                }
            }
            sortSpans(spans, result);
        }

        return result;
    }

    default List<SpanNode> sortTreeSpans(List<SpanNode> spans) {
        List<SpanNodeTreeRes> tree = TrackerTreeUtil.treeWrap(spans);
        if (CollUtil.isEmpty(tree)) {
            return spans;
        }

        List<SpanNode> result = new ArrayList<>();
        sortTreeSpans1(tree, result);
        return result;
    }

    default void sortTreeSpans1(List<SpanNodeTreeRes> tree, List<SpanNode> result) {
        for (SpanNodeTreeRes spanNodeTreeRes : tree) {
            SpanNode spanNode = BeanUtil.toObj(spanNodeTreeRes, SpanNode.class);
            spanNode.setSpanInterval(spanNodeTreeRes.getSpanInterval());
            result.add(spanNode);
            if (CollUtil.isNotEmpty(spanNodeTreeRes.getChildren())) {
                sortTreeSpans1(spanNodeTreeRes.getChildren(), result);
            }
        }
    }
}
